package dk.eclipse.android.badassapp.distance;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

import dk.eclipse.android.badassapp.R;
import dk.eclipse.android.badassapp.helper.LocationHelper;
import dk.eclipse.android.badassapp.helper.MyHelper;

/**
 * @author jaw
 * 
 *         Supports a map with yourself and your target(s) displayed if
 *         geolocations are available.
 * 
 */
public class DistanceActivity extends MapActivity {
	private static final String LOG_TAG = DistanceActivity.class.getSimpleName();

	// Map uri -> position, label etc.
	public static final ConcurrentMap<String, ObjectOnMap> mObjectsOnMap = new ConcurrentHashMap<String, ObjectOnMap>();

	




	/**
	 * Listens to location updates.
	 */
	private LocationListener locationListener = new LocationListener() {

		public void onLocationChanged(Location location) {
			
			mMapView.getController().animateTo(
					new GeoPoint((int) (location.getLatitude() * 1e6),
							((int) (location.getLongitude() * 1e6))));
			if (!mObjectsOnMap.containsKey(TargetUpdateReceiver.ME_URI)) {
				mObjectsOnMap.put(TargetUpdateReceiver.ME_URI, new ObjectOnMap(
						location.getLatitude(), location.getLongitude(), "Me",
						TargetUpdateReceiver.ME_URI));
			} else {
				ObjectOnMap objectOnMap = mObjectsOnMap
						.get(TargetUpdateReceiver.ME_URI);
				objectOnMap.mLatitude = location.getLatitude();
				objectOnMap.mLongitude = location.getLongitude();
			}
			updateLocation(new MapRectangle(mObjectsOnMap.values()));
		}

		// Not used.
		public void onProviderDisabled(String provider) {}
		public void onProviderEnabled(String provider) {}
		public void onStatusChanged(String provider, int status, Bundle extras) {}
	};

	private MapView mMapView;

	private Drawable mDrawableMe;

	private Drawable mDrawableTarget;

	private void centerMapBetweenMeAndTarget(MapRectangle mapRectangle) {
		MapController controller = mMapView.getController();
		controller.zoomToSpan(mapRectangle.getLatitudeSpan(),
				mapRectangle.getLongitudeSpan());
		controller.animateTo(mapRectangle.getCenter());
	}

	private List<MyItemizedOverlay> createItemizedOverlays(
			MapRectangle mapRectangle) {
		List<MyItemizedOverlay> rv = new ArrayList<MyItemizedOverlay>(
				mObjectsOnMap.size());

		Collection<ObjectOnMap> collection = mObjectsOnMap.values();
		for (ObjectOnMap objectOnMap : collection) {
			GeoPoint point = new GeoPoint((int) (objectOnMap.mLatitude * 1e6),
					(int) (objectOnMap.mLongitude * 1e6));

			Drawable drawable = TargetUpdateReceiver.ME_URI
					.equals(objectOnMap.mUri) ? mDrawableMe : mDrawableTarget;

			MyItemizedOverlay myItemizedOverlay = new MyItemizedOverlay(
					DistanceActivity.this, drawable, mMapView);
			MyOverlayItem overlayItem = new MyOverlayItem(point,
					objectOnMap.mLabel, objectOnMap.mLatitude + ", "
							+ objectOnMap.mLongitude, objectOnMap.mUri);
			myItemizedOverlay.addOverlay(overlayItem);
			rv.add(myItemizedOverlay);
		}
		return rv;
	}

	protected boolean isRouteDisplayed() {
		return false;
	}

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_LEFT_ICON);
		setContentView(R.layout.tab_distance);
		setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.ic_app);

		Resources resources = this.getResources();
		mDrawableMe = resources.getDrawable(R.drawable.androidmarker);
		mDrawableTarget = resources.getDrawable(
				R.drawable.tab_target_on);
		
		LocationHelper.setContext(this);

		mMapView = (MapView) findViewById(R.id.mapview);
		// automatic updating keeps resetting zoom, so we don't want it, when
		// looking at two fixed points
		mMapView.setBuiltInZoomControls(true);

	}
	
	protected void onStart() {
		super.onStart();

			newOrupdatedObjectOnMap(TargetUpdateReceiver.ME_URI,
					MyHelper.getLatitude(this), MyHelper.getLongitude(this), "Me");
	}

	/**
	 * Creates new ItemizedOverlays and _replaces_ them with any existing
	 * overlays in mapview. Replacing the overlays may not be necessary. I have
	 * seen no performance hit in this context.
	 * 
	 * @param mapRectangle
	 */
	private void updateLocation(MapRectangle mapRectangle) {
		List<MyItemizedOverlay> createItemizedOverlays = createItemizedOverlays(mapRectangle);
		List<Overlay> overlays = mMapView.getOverlays();
		overlays.clear();
		overlays.addAll(createItemizedOverlays);
		centerMapBetweenMeAndTarget(mapRectangle);
	}

	public void onResume() {
		super.onResume();
			newOrupdatedObjectOnMap(TargetUpdateReceiver.ME_URI,
					MyHelper.getLatitude(this), MyHelper.getLongitude(this), "Me");
			
		LocationHelper.addListener(locationListener);
		updateLocation(new MapRectangle(mObjectsOnMap.values()));
	}

	public void onPause() {
		super.onPause();
		LocationHelper.removeListener(locationListener);
	}

	//-----------------------------------
	// Functions exposing internal state.
	// Functions exposing internal state.
	//-----------------------------------
	// The internal state is mainly a list of
	// objects that are shown on the map.
	// The objects are kept in objectsOnMap.
	//
	// TODO : This does not update when a new target appears on the map, unless
	// :
	// 1 a gps update happens
	// 2 onStart, onResume or onCreate is called
	//
	// I would like to be able to invalidate the view and override onDraw to
	// include drawing my objects on the map. However the MapView does not allow
	// overriding onDraw and I have not prioritised this at the moment.

	public static synchronized void newObjectOnMap(String uri, double latitude,
			double longitude, String label) {
		ObjectOnMap objectOnMap = new ObjectOnMap(latitude, longitude, label,
				uri);
		mObjectsOnMap.put(uri, objectOnMap);
		
	}

	public static synchronized void updateObjectOnMap(String uri,
			double latitude, double longitude) {
		ObjectOnMap s = mObjectsOnMap.get(uri);
		if (s != null) {
			s.mLatitude = latitude;
			s.mLongitude = longitude;
		} else {
			Log.e(LOG_TAG, "Unable to find uri [" + uri + "]");
		}
	}

	public static synchronized void newOrupdatedObjectOnMap(String uri,
			double latitude, double longitude, String label) {
		ObjectOnMap objectOnMap = new ObjectOnMap(latitude, longitude, label,
				uri);
		mObjectsOnMap.put(uri, objectOnMap);
	}

	public static synchronized void removeObjectOnMap(String uri) {
		mObjectsOnMap.remove(uri);
	}

}
